/**
*		@file NextBip.cpp
*		@brief NextBip class
*
*		@author Lan Ziquan
*
*		@date 3/3/2012
*/

#include "NextBip.h"

vector<StmtSet>	NextBip::next_bip_table_;
vector<StmtSet>	NextBip::next_bip_table_t_;
vector<StmtSet>	NextBip::next_bip_adv_table_;
vector<StmtSet>	NextBip::next_bip_adv_cache_table_;

StmtSet			NextBip::next_bip_has_s0_;
StmtSet			NextBip::next_bip_has_0s_;
bool			NextBip::next_bip_has_00_;

static StmtSet BranchBack(ProcIdx proc_idx_from)
{
	StmtSet result(StmtTable::GetSize(),false);
	for( int i = 0; i < StmtTable::GetSizeCall(); i++ )
	{
		if( StmtTable::GetCallTNode(i+1)->value == proc_idx_from )
		{
			if( StmtTable::GetCallGNode(i+1)->next_a != NULL )
			{
				result[StmtTable::GetCallGNode(i+1)->next_a->stmt_num - 1] = true;
			}
			else
			{
				result = LogicOR<StmtSet>(result, BranchBack(StmtTable::GetProcIdx(StmtTable::GetCallTNode(i+1)->stmt_num)));
			}
		}
	}
	return result;
}

void	NextBip::RebuildNextBip()
{
	next_bip_table_.resize(StmtTable::GetSize());
	next_bip_table_t_.resize(StmtTable::GetSize());
	next_bip_adv_table_.resize(StmtTable::GetSize());
	next_bip_adv_cache_table_.resize(StmtTable::GetSize());
	for( int i = 0; i < StmtTable::GetSize(); i++ )
	{
		next_bip_table_[i].resize(StmtTable::GetSize());
		next_bip_table_t_[i].resize(StmtTable::GetSize());
		next_bip_adv_table_[i].resize(StmtTable::GetSize());
		next_bip_adv_cache_table_[i].resize(StmtTable::GetSize());
	}

	for( int i = 0; i < StmtTable::GetSize(); i++ )
	{
		if( StmtTable::GetStmtGNode(i+1)->next_a != NULL )
			next_bip_table_[i][StmtTable::GetStmtGNode(i+1)->next_a->stmt_num - 1] = true;
		if( StmtTable::GetStmtGNode(i+1)->next_b != NULL )
			next_bip_table_[i][StmtTable::GetStmtGNode(i+1)->next_b->stmt_num - 1] = true;
	}
	//======================= NEW part than Next.cpp =========================

	for( int i = 0; i < StmtTable::GetSizeCall(); i++ )
	{
		for( int j = 0; j < StmtTable::GetSize(); j++ )
			next_bip_table_[StmtTable::GetCallTNode(i+1)->stmt_num - 1][j] = false;
		next_bip_table_[StmtTable::GetCallTNode(i+1)->stmt_num - 1][ProcTable::GetStmtNumStart(StmtTable::GetCallTNode(i+1)->value) - 1] = true;
	}

	for( int i = 0; i < StmtTable::GetSizeAssign(); i++ )
		if( StmtTable::GetAssignGNode(i+1)->next_a == NULL )
			next_bip_table_[StmtTable::GetAssignTNode(i+1)->stmt_num - 1] = BranchBack(StmtTable::GetProcIdx(StmtTable::GetAssignTNode(i+1)->stmt_num));

	for( int i = 0; i < StmtTable::GetSizeWhile(); i++ )
		if( StmtTable::GetWhileGNode(i+1)->next_b == NULL )
		{
			next_bip_table_[StmtTable::GetWhileTNode(i+1)->stmt_num - 1] = BranchBack(StmtTable::GetProcIdx(StmtTable::GetWhileTNode(i+1)->stmt_num));
			next_bip_table_[StmtTable::GetWhileTNode(i+1)->stmt_num - 1][StmtTable::GetWhileGNode(i+1)->next_a->stmt_num - 1] = true;
		}

	//================================== END ===================================
	for( int i = 0; i < StmtTable::GetSize(); i++ )
		for( int j = 0; j < StmtTable::GetSize(); j++ )
			next_bip_table_t_[i][j] = next_bip_table_[j][i];

	next_bip_has_s0_.resize(StmtTable::GetSize());
	next_bip_has_0s_.resize(StmtTable::GetSize());
	next_bip_has_00_ = false;
	return;
}

void NextBip::CompleteNextBip()
{
	for( int i = 0; i < StmtTable::GetSize(); i++ )	next_bip_has_s0_[i] = IsAny<StmtSet>(next_bip_table_[i]);
	for( int i = 0; i < StmtTable::GetSize(); i++ )	next_bip_has_0s_[i] = IsAny<StmtSet>(next_bip_table_t_[i]);
	next_bip_has_00_ = IsAny<StmtSet>(next_bip_has_s0_);
	return;
}

bool NextBip::IsNextBip(StmtNum stmt_num_left, StmtNum stmt_num_right)
{
	if( stmt_num_left > StmtTable::GetSize() || stmt_num_right > StmtTable::GetSize() )
		return false;
	if( stmt_num_left == 0 )
	{
		if( stmt_num_right == 0 )
			return next_bip_has_00_;
		else
			return next_bip_has_0s_[stmt_num_right - 1];
	}
	else
	{
		if( stmt_num_right == 0 )
			return next_bip_has_s0_[stmt_num_left - 1];
		else
		{
			return next_bip_table_[stmt_num_left - 1][stmt_num_right - 1];
		}
	}
}

bool NextBip::IsNextBipAdv(StmtNum stmt_num_left, StmtNum stmt_num_right)
{
	if( stmt_num_left > StmtTable::GetSize() || stmt_num_right > StmtTable::GetSize() )
		return false;
	if( stmt_num_left == 0 )
	{
		if( stmt_num_right == 0 )
			return next_bip_has_00_;
		else
			return next_bip_has_0s_[stmt_num_right - 1];
	}
	else
	{
		if( stmt_num_right == 0 )
			return next_bip_has_s0_[stmt_num_left - 1];
		else
		{
			bool b_read_in;
			if( ReadAndReturnNextBipAdv(stmt_num_left, stmt_num_right, b_read_in) )
				return b_read_in;
			if( next_bip_adv_cache_table_[stmt_num_left - 1][stmt_num_right - 1] )
				return next_bip_adv_table_[stmt_num_left - 1][stmt_num_right - 1];
			if( ProcTable::IsProcPath(StmtTable::GetProcIdx(stmt_num_left), StmtTable::GetProcIdx(stmt_num_right)) )
			{
				return WriteAndReturnNextBipAdv(stmt_num_left, stmt_num_right, true);
			}
			if( Next::IsNextAdv(stmt_num_left, stmt_num_right) )
			{
				return WriteAndReturnNextBipAdv(stmt_num_left, stmt_num_right, true);
			}
			if( !Calls::IsCallsAdv(StmtTable::GetProcIdx(stmt_num_left), StmtTable::GetProcIdx(stmt_num_right)) &&
				!Calls::IsCallsAdv(StmtTable::GetProcIdx(stmt_num_right), StmtTable::GetProcIdx(stmt_num_left)) )
			{
				return WriteAndReturnNextBipAdv(stmt_num_left, stmt_num_right, false);
			}
			for( int i = 0; i < StmtTable::GetSizeCall(); i++ )
			{
				if( StmtTable::GetProcIdx(StmtTable::GetCallTNode(i+1)->stmt_num) == StmtTable::GetProcIdx(stmt_num_left) &&
					( Calls::IsCallsAdv(StmtTable::GetCallTNode(i+1)->value,StmtTable::GetProcIdx(stmt_num_right)) ||
					StmtTable::GetCallTNode(i+1)->value == StmtTable::GetProcIdx(stmt_num_right)) &&
					( Next::IsNextAdv(stmt_num_left, StmtTable::GetCallTNode(i+1)->stmt_num) ||
					stmt_num_left == StmtTable::GetCallTNode(i+1)->stmt_num) )
				{
					return WriteAndReturnNextBipAdv(stmt_num_left, stmt_num_right, true);
				}
				if( StmtTable::GetProcIdx(StmtTable::GetCallTNode(i+1)->stmt_num) == StmtTable::GetProcIdx(stmt_num_right) &&
					( Calls::IsCallsAdv(StmtTable::GetCallTNode(i+1)->value,StmtTable::GetProcIdx(stmt_num_left)) ||
					StmtTable::GetCallTNode(i+1)->value == StmtTable::GetProcIdx(stmt_num_left)) &&
					Next::IsNextAdv(StmtTable::GetCallTNode(i+1)->stmt_num, stmt_num_right) )
				{
					return WriteAndReturnNextBipAdv(stmt_num_left, stmt_num_right, true);
				}
			}
			return WriteAndReturnNextBipAdv(stmt_num_left, stmt_num_right, false);
		}
	}
}

bool NextBip::ReadAndReturnNextBipAdv(StmtNum stmt_num_left, StmtNum stmt_num_right, bool& b_read_in)
{
	QueryThread::GetLockNextBip();
	bool result;
	if( next_bip_adv_cache_table_[stmt_num_left - 1][stmt_num_right - 1] )
	{
		b_read_in = next_bip_adv_table_[stmt_num_left - 1][stmt_num_right - 1];
		result = true;
	}
	else {result = false;}
	QueryThread::ReleaseLockNextBip();
	return result;
}

bool NextBip::WriteAndReturnNextBipAdv(StmtNum stmt_num_left, StmtNum stmt_num_right, bool b_set_to)
{
	QueryThread::GetLockNextBip();
	next_bip_adv_table_[stmt_num_left - 1][stmt_num_right - 1] = b_set_to;
	next_bip_adv_cache_table_[stmt_num_left - 1][stmt_num_right - 1] = true;
	QueryThread::ReleaseLockNextBip();
	return b_set_to;
}

string NextBip::ToString()
{
	stringstream ss;
	ss << "NextBip : " << next_bip_has_00_ << endl;

	for( int i = 0; i < StmtTable::GetSize(); i++ )
	{
		ss << (i+1) << " :[" << next_bip_has_s0_[i] << "]";
		for( int j = 0; j < StmtTable::GetSize(); j++ )
			if( next_bip_table_[i][j] )
				ss << " " << (j+1);
		ss << endl;
	}

	ss<< endl << endl;

	for( int i = 0; i < StmtTable::GetSize(); i++ )
	{
		ss << (i+1) << " :[" << next_bip_has_0s_[i] << "]";
		for( int j = 0; j < StmtTable::GetSize(); j++ )
			if( next_bip_table_t_[i][j] )
				ss << " " << (j+1);
		ss << endl;
	}

	ss<< endl << endl;

	for( int i = 0; i < StmtTable::GetSize(); i++ )
	{
		ss << (i+1) << " : ";
		for( int j = 0; j < StmtTable::GetSize(); j++ )
		{
			if( next_bip_adv_cache_table_[i][j] )
				ss << " " << next_bip_adv_table_[i][j];
			else
				ss << " _";
		}
		ss << endl;
	}

	return ss.str();
}